#include "tbcore/reflection/transform.hpp"

#include "tbcore/base/arbitrary_cast.hpp"
#include "tbcore/base/unique_id.hpp"

#include "tbcore/reflection/variant.hpp"
#include "tbcore/reflection/type_info.hpp"
#include "tbcore/reflection/reflection.hpp"

TB_NAMESPACE_BEGIN

static const int32 kPrettyStringifyTransformBufferSize = 4096;

_STD string TB::BuildinTypeToString(const Variant* val) {
  TB_ASSERT(val->IsBuildinType());
  switch (val->TypeId()) {
    case Variant::kBoolType: {
      return ArbitraryCast<_STD string>(val->Value<bool>());
    }
    case Variant::kInt8Type: {
      return ArbitraryCast<_STD string>(val->Value<int8>());
    }
    case Variant::kUint8Type: {
      return ArbitraryCast<_STD string>(val->Value<uint8>());
    }
    case Variant::kInt16Type: {
      return ArbitraryCast<_STD string>(val->Value<int16>());
    }
    case Variant::kUint16Type: {
      return ArbitraryCast<_STD string>(val->Value<uint16>());
    }
    case Variant::kInt32Type: {
      return ArbitraryCast<_STD string>(val->Value<int32>());
    }
    case Variant::kUint32Type: {
      return ArbitraryCast<_STD string>(val->Value<uint32>());
    }
    case Variant::kInt64Type: {
      return ArbitraryCast<_STD string>(val->Value<int64>());
    }
    case Variant::kUint64Type: {
      return ArbitraryCast<_STD string>(val->Value<uint64>());
    }
    case Variant::kFloatType: {
      return ArbitraryCast<_STD string>(val->Value<float>());
    }
    case Variant::kDoubleType: {
      return ArbitraryCast<_STD string>(val->Value<double>());
    }
    case Variant::kDurationType: {
      return ArbitraryCast<_STD string>(val->Value<int64>());
    }
    case Variant::kDatetimeType: {
      return ArbitraryCast<_STD string>(val->Value<int64>());
    }
    case Variant::kEnumType: {
      return ArbitraryCast<_STD string>(val->Value<uint32>());
    }
    case Variant::kStringType: {
      return val->Value<_STD string>();
    }
  }
  return "...";
}

struct PrettyStringifyTransform::PrettyStringifyTransformImpl {
  PrettyStringifyTransformImpl() : indent_(-1) {}

  void PrettyPrintf(const char* format, ...) {
    int wsize = 0;
    // use two space instead of tab
    wsize = snprintf(buf_, kPrettyStringifyTransformBufferSize, "%s", _STD string("  ", indent_).c_str());
    TB_ASSERT(wsize > 0 && wsize <= kPrettyStringifyTransformBufferSize);
    oss_ << _STD string(buf_, wsize);
    va_list arg_ptr;
    va_start(arg_ptr, format);
    wsize = vsnprintf(buf_, kPrettyStringifyTransformBufferSize, format, arg_ptr);
    TB_ASSERT(wsize > 0 && wsize <= kPrettyStringifyTransformBufferSize);
    oss_ << _STD string(buf_, wsize);
  }

  int32 indent_;
  char buf_[kPrettyStringifyTransformBufferSize];
  _STD ostringstream oss_;
};

PrettyStringifyTransform::PrettyStringifyTransform() 
  : impl_(new PrettyStringifyTransformImpl()) {}

PrettyStringifyTransform::~PrettyStringifyTransform() {
  TB_SAFE_DELETE(impl_);
}

_STD string PrettyStringifyTransform::Stringify(const Variant* val){
  Apply(*val, *this);
  return impl_->oss_.str();
}

void PrettyStringifyTransform::Begin(const reflection::TypeInfo* info, uint32) {
  impl_->indent_ += 1;
  impl_->PrettyPrintf("%s:\n", info->name.c_str());
}

void PrettyStringifyTransform::Field(const reflection::Property* prop, const Variant* val) {
  impl_->PrettyPrintf("%s -> %s\n", prop->name.c_str(), BuildinTypeToString(val));
}

void PrettyStringifyTransform::End() {}

TB_NAMESPACE_END